package pl.edu.agh.cp;

import pl.edu.agh.cp.learning.ITeacherLearningFunction;
import pl.edu.agh.neural.core.ConnectedNeuron;
import pl.edu.agh.neural.core.ILayer;
import pl.edu.agh.neural.core.INeuron;
import pl.edu.agh.neural.core.ITrainableLayerWithTeacherOLD;
import pl.edu.agh.neural.simple.InputConnection;
import pl.edu.agh.neural.simple.SimpleNeuron;
import pl.edu.agh.neural.simple.activation.IActivationFunction;
import pl.edu.agh.som.KohonenMap;
import pl.edu.agh.som.Node;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * Created with IntelliJ IDEA.
 * User: Goku122
 * Date: 15.12.12
 * Time: 13:40
 * To change this template use File | Settings | File Templates.
 */
public class GrossbergLayer implements ITrainableLayerWithTeacherOLD
{
    private Random rand = new Random();
    private int nodeNumber;
    private ITeacherLearningFunction learningFunction;
    private ILayer previousLayer;
    private ConnectedNeuron[] nodes;
    private double[] teachingValues;

    public GrossbergLayer(int nodeNumber,
                          ITeacherLearningFunction learningFunction,
                          ILayer previousLayer)
    {
        this.nodeNumber = nodeNumber;
        this.learningFunction = learningFunction;
        this.previousLayer = previousLayer;

        nodes = new ConnectedNeuron[this.nodeNumber];

        IActivationFunction activationFunction = learningFunction.getActivationFunction();

        for (int i = 0; i < this.nodeNumber; i++)
        {
            InputConnection[] inputConnections = createInputConnections(this.previousLayer);
            nodes[i] = new SimpleNeuron(activationFunction, inputConnections);
        }
    }

    private InputConnection[] createInputConnections(ILayer previousLayer)
    {
        List<? extends INeuron> neurons = previousLayer.getNeurons();
        InputConnection[] inputConnections = new InputConnection[neurons.size()];
        for (int i = 0; i < neurons.size(); i++)
        {
            inputConnections[i] = new InputConnection(neurons.get(i), randomDoubleFromRange(0.0, 1.0));
        }
        return inputConnections;
    }

    private double randomDoubleFromRange(double min, double max)
    {
        return min + (max - min) * rand.nextDouble();
    }

    @Override
    public void train(int step)
    {
        KohonenMap previous = null;
        if (previousLayer instanceof KohonenMap)
        {
            previous = (KohonenMap) previousLayer;
        }
        learningFunction.teach(step, nodes, teachingValues, previous.evaluateAndGetWinner());
    }

    public void train(int step, Node winner)
    {
        learningFunction.teach(step, nodes, teachingValues, winner);
    }

    @Override
    public List<? extends INeuron> getNeurons()
    {
        return Arrays.asList(nodes);
    }

    @Override
    public double[] evaluate()
    {
        double[] result = new double[nodes.length];
        int i = 0;
        for (ConnectedNeuron neuron : nodes)
        {
            result[i] = neuron.evaluate();
            i++;
        }
        return result;
    }

    @Override
    public void setTeachingValues(double[] values)
    {
        this.teachingValues = values;
    }
}
